﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace MVCPets.Extensions
{
  public static class StringExtensions
  {
    #region ReverseString
    /// <summary>
    /// Reverses the string
    /// </summary>
    /// <param name="input">String to reverse</param>
    /// <returns>The string reversed</returns>
    public static string ReverseString(this string input)
    {
      return new string(input.ToCharArray().Reverse().ToArray());
    }
    #endregion

    #region EscapeJS
    public static string EscapeJs(this string data)
    {
      data = data.Replace("'", @"\'");
      data = data.Replace("\"", @"\""'");
      
      return data;
    }
    #endregion

    #region Scramble String
    public static string ScrambleString(this string input)
    {
      var rnd = new Random();
      var array = input.ToCharArray();

      for (int i = 0; i < input.Length * 100; i++)
      {
        var c = rnd.Next(0, array.Length - 1);
        var t = rnd.Next(0, array.Length - 1);
        var hold = array[c];

        array[c] = array[t];
        array[t] = hold;
      }

      return new string(array);
    }
    #endregion

    #region Compression Methods
    public static string Compress(this string text)
    {
      byte[] buffer = Encoding.UTF8.GetBytes(text);
      MemoryStream ms = new MemoryStream();

      using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
      {
        zip.Write(buffer, 0, buffer.Length);
      }

      ms.Position = 0;
      MemoryStream outStream = new MemoryStream();

      byte[] compressed = new byte[ms.Length];
      ms.Read(compressed, 0, compressed.Length);

      byte[] gzBuffer = new byte[compressed.Length + 4];
      System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
      System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);

      return Convert.ToBase64String(gzBuffer);
    }

    public static string Decompress(this string text)
    {
      byte[] gzBuffer = Convert.FromBase64String(text);

      using (MemoryStream ms = new MemoryStream())
      {
        int msgLength = BitConverter.ToInt32(gzBuffer, 0);
        ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

        byte[] buffer = new byte[msgLength];

        ms.Position = 0;

        using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
        {
          zip.Read(buffer, 0, buffer.Length);
        }

        return Encoding.UTF8.GetString(buffer);
      }
    }
    #endregion

    #region Private Members
    // The key used for generating the encrypted string. 
    // DO NOT CHANGE THIS VALUE, EVER!!!!!
    private static readonly string cryptoKey = "R3scu3S1t3";
    // The Initialization Vector for the DES encryption routine
    private static readonly byte[] IV = new byte[8] { 240, 3, 45, 29, 0, 76, 173, 59 };
    #endregion

    #region Encryption Methods
    /// <summary>
    /// Encrypts a string
    /// </summary>
    /// <param name="val"></param>
    /// <returns></returns>
    public static string Encrypt(this string val)
    {
      return val.Encrypt(cryptoKey);
    }

    /// <summary>
    /// Encrypts a string using the specified key
    /// </summary>
    /// <param name="Value">
    /// A string to Encrypt
    /// </param>
    /// <param name="key">The key to use for the encryption</param>
    /// <returns>
    /// Encrypted version of the string passed in
    /// </returns>
    public static string Encrypt(this string val, string key)
    {
      var buffer = val.GetBytes();
      var des = new TripleDESCryptoServiceProvider();
      des.Key = key.GetBytes().ComputeHash();
      des.IV = IV;

      return des.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length).ToBase64String();
    }

    /// <summary>
    /// Decrypts a string that was encrypted with the Encrypt method
    /// </summary>
    /// <param name="val"></param>
    /// <returns></returns>
    public static string Decrypt(this string val)
    {
      return val.Decrypt(cryptoKey);
    }

    /// <summary>
    /// Decrypts a string that was Encrypted with the Encrypt method using the provided key
    /// </summary>
    /// <param name="Value">
    /// A string to Decrypt
    /// </param>
    /// <param name="key">the key that was used to encrypt the string</param>
    /// <returns>
    /// Decrypted version of the Encrypted string passed in
    /// </returns>
    public static string Decrypt(this string val, string key)
    {
      string ret;

      try
      {
        var buffer = val.FromBase64String();
        var des = new TripleDESCryptoServiceProvider();
        des.Key = key.GetBytes().ComputeHash();
        des.IV = IV;

        ret = des.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length).GetString();
      }
      catch
      {
        ret = string.Empty;
      }

      return ret;
    }
    #endregion

    #region Encoding/Decoding
    #region Url
    public static string UrlEncode(this string text)
    {
      return HttpUtility.UrlEncode(text);
    }

    public static string UrlDecode(this string text)
    {
      return HttpUtility.UrlDecode(text);
    }
    #endregion

    #region Html
    public static string HtmlEncode(this string text)
    {
      return HttpUtility.HtmlEncode(text);
    }

    public static string HtmlDecode(this string text)
    {
      return HttpUtility.HtmlDecode(text);
    }
    #endregion

    #region Base64
    public static byte[] FromBase64String(this string value)
    {
      return Convert.FromBase64String(value);
    }

    public static byte[] GetBytes(this string value)
    {
      return System.Text.ASCIIEncoding.ASCII.GetBytes(value);
    }
    #endregion

    #region ISO-8859
    public static string ToIso8859String(this string src)
    {
      return Encoding.GetEncoding("iso8859-1").GetString(Encoding.UTF8.GetBytes(src));
    }

    public static string FromIso8859String(this string src)
    {
      return Encoding.UTF8.GetString(Encoding.GetEncoding("iso8859-1").GetBytes(src));
    }
    #endregion
    #endregion

    #region Contains
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
      return source.IndexOf(toCheck, comp) >= 0;
    }

    public static bool Contains(this string source, string toCheck, RegexOptions comp)
    {
      return Regex.Match(source, toCheck, comp).Groups.Count > 0;
    }
    #endregion

    #region IsDate
    /// <summary>
    /// Verifies if the string is a date
    /// </summary>
    /// <param name="dt">The dt.</param>
    /// <returns>
    /// 	<c>true</c> if the specified dt is date; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsDate(this string dt)
    {
      DateTime newDate;
      return DateTime.TryParse(dt, out newDate);
    }
    #endregion

    #region ExtractBefore
    /// <summary>
    /// Pulls out everything before the value in the string
    /// </summary>
    /// <param name="input">The string to look at</param>
    /// <param name="value">The value to find in the string</param>
    /// <returns>Everything from the input string up to value</returns>
    public static string ExtractBefore(this string input, string value)
    {
      int start = 0;
      int length = input.IndexOf(value);

      return input.Substring(start, length);
    }
    #endregion

    #region ExtractAfter
    /// <summary>
    /// Extracts the string after the specified value
    /// </summary>
    /// <param name="input">The string to look at</param>
    /// <param name="value">The string to extract after</param>
    /// <returns>the end of the string after the specified value</returns>
    public static string ExtractAfter(this string input, string value)
    {
      int start = input.IndexOf(value) + value.Length;
      int length = input.Length - input.IndexOf(value) - value.Length;

      return input.Substring(start, length);
    }
    #endregion

    #region EqualsIgnoreCase
    /// <summary>
    /// Determines if the two strings are equal, ignoring case differences
    /// </summary>
    /// <param name="input">first string to compare</param>
    /// <param name="value">second string to compare</param>
    /// <returns>bool result</returns>
    public static bool EqualsIgnoreCase(this string input, string value)
    {
      return input.Equals(value, StringComparison.InvariantCultureIgnoreCase);
    }
    #endregion

    #region HasValue
    /// <summary>
    /// The opposite of IsNullOrEmpty.
    /// </summary>
    /// <param name="value">The string to check</param>
    /// <returns>bool result</returns>
    public static bool HasValue(this string value)
    {
      return !string.IsNullOrEmpty(value);
    }
    #endregion

    #region IsNullOrEmpty
    /// <summary>
    /// Determines if a string is null or empty
    /// </summary>
    /// <param name="value">The string to check</param>
    /// <returns>bool to indicate if the string is null or empty</returns>
    public static bool IsNullOrEmpty(this string value)
    {
      return string.IsNullOrEmpty(value);
    }
    #endregion

    #region Palindrome
    public static bool IsPalindrome(this string value)
    {
      return value == value.ReverseString();
    }
    #endregion

    #region Right
    /// <summary>
    /// Returns the last few characters of the string with a length
    /// specified by the given parameter. If the string's length is less than the 
    /// given length the complete string is returned. If length is zero or 
    /// less an empty string is returned
    /// </summary>
    /// <param name="s">the string to process</param>
    /// <param name="length">Number of characters to return</param>
    /// <returns></returns>
    public static string Right(this string s, int length)
    {
      length = Math.Max(length, 0);

      if (s.Length > length) return s.Substring(s.Length - length, length);
      else return s;
    }
    #endregion

    #region SubstringIndex
    /// <summary>
    /// Returns the first part of the string up "find"
    /// </summary>
    /// <param name="s">String being manipulated</param>
    /// <param name="find">String to find</param>
    /// <returns>The first part of the string up to. If it is not found you get back the base string.</returns>
    public static string SubstringIndex(this string s, string find)
    {
      var ret = s.Left(s.IndexOf(find));

      if (ret.IsNullOrEmpty()) ret = s;

      return ret;
    }
    #endregion

    #region Left
    /// <summary>
    /// Returns the first few characters of the string with a length
    /// specified by the given parameter. If the string's length is less than the 
    /// given length the complete string is returned. If length is zero or 
    /// less an empty string is returned
    /// </summary>
    /// <param name="s">the string to process</param>
    /// <param name="length">Number of characters to return</param>
    /// <returns></returns>
    public static string Left(this string s, int length)
    {
      length = Math.Max(length, 0);

      if (s.Length > length) return s.Substring(0, length);
      else return s;
    }
    #endregion

    #region Truncate
    /// <summary>
    /// Truncates the string to a specified length and replace the truncated to a ...
    /// </summary>
    /// <param name="text">string that will be truncated</param>
    /// <param name="maxLength">total length of characters to maintain before the truncate happens</param>
    /// <returns>truncated string</returns>
    public static string Truncate(this string text, int maxLength)
    {
      // replaces the truncated string to a ...
      const string suffix = "...";
      string truncatedString = text;

      if (maxLength <= 0) return truncatedString;
      int strLength = maxLength - suffix.Length;

      if (strLength <= 0) return truncatedString;

      if (text == null || text.Length <= maxLength) return truncatedString;

      truncatedString = text.Substring(0, strLength);
      truncatedString = truncatedString.TrimEnd();
      truncatedString += suffix;
      return truncatedString;
    }
    #endregion

    #region Parse
    /// <summary>
    /// Parses the specified string as T.
    /// You will need to do your own exception handling.
    /// </summary>
    /// <typeparam name="T">The type to convert the string to</typeparam>
    /// <param name="value">The string to convert</param>
    /// <returns>The T result</returns>
    public static T Parse<T>(this string value)
    {
      // Get default value for type so if string
      // is empty then we can return default value.
      T result = default(T);

      if (!string.IsNullOrEmpty(value))
      {
        // we are not going to handle exception here
        // if you need SafeParse then you should create
        // another method specially for that.
        TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
        result = (T)tc.ConvertFrom(value);
      }

      return result;
    }

    /// <summary>
    /// Does a safe parse of the string and returns T.
    /// This method eats any exceptions and returns the default value for T if an exception occurs.
    /// </summary>
    /// <typeparam name="T">The type to convert the string to</typeparam>
    /// <param name="value">The string to convert</param>
    /// <returns>The T result</returns>
    public static T SafeParse<T>(this string value)
    {
      // Get default value for type so if string
      // is empty then we can return default value.
      T result = default(T);

      if (!string.IsNullOrEmpty(value))
      {
        // we are not going to handle exception here
        // if you need SafeParse then you should create
        // another method specially for that.
        try
        {
          TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
          result = (T)tc.ConvertFrom(value);
        }
        catch
        {
          result = default(T);
        }
      }

      return result;
    }
    #endregion    

    #region Format
    /// <summary>
    /// Formats the string with 
    /// </summary>
    /// <param name="format">The string that that will be formatted</param>
    /// <param name="arg">An argument passed to string.format</param>
    /// <param name="additionalArgs">additional params for string.format</param>
    /// <returns></returns>
    public static string FormatString(this string format, object arg, params object[] additionalArgs)
    {
      string ret = null;

      if (additionalArgs == null || additionalArgs.Length == 0) ret = string.Format(format, arg);
      else ret = string.Format(format, new object[] { arg }.Concat(additionalArgs).ToArray());

      return ret;
    }

    /// <summary>
    /// Formats the args using String.Format with the target string as a format string.
    /// </summary>
    /// <param name="fmt">The format string passed to String.Format</param>
    /// <param name="args">The args passed to String.Format</param>
    /// <returns></returns>
    public static string ToFormattedString(this string fmt, params object[] args)
    {
      return String.Format(fmt, args);
    }
    #endregion

    #region File Methods
    public static string ReadAllText(this string path)
    {
      return File.ReadAllText(path);
    }

    public static string[] ReadAllLines(this string path)
    {
      return File.ReadAllLines(path);
    }

    public static byte[] ReadAllBytes(this string path)
    {
      return File.ReadAllBytes(path);
    }
    #endregion

    #region Methods lifted from Subsonic
    /// <summary>
    /// Converts ASCII encoding to Unicode
    /// </summary>
    /// <param name="asciiCode">The ASCII code.</param>
    /// <returns></returns>
    public static string AsciiToUnicode(this int asciiCode)
    {
      var ascii = Encoding.UTF32;

      var b = ascii.GetBytes(asciiCode.Character().ToString());

      return ascii.GetString((b));
    }

    /// <summary>
    /// Converts a generic List collection to a single string using the specified delimitter.
    /// </summary>
    /// <param name="list">The list.</param>
    /// <param name="delimiter">The delimiter.</param>
    /// <returns></returns>
    public static string ToDelimitedList(this IEnumerable<string> list, string delimiter)
    {
      var sb = new StringBuilder();

      foreach (string s in list) sb.Append(s.Concatinate(delimiter));

      return sb.ToString().Chop();
    }

    public static bool Matches(this string source, string compare)
    {
      return String.Equals(source, compare, StringComparison.InvariantCultureIgnoreCase);
    }

    public static bool MatchesTrimmed(this string source, string compare)
    {
      return String.Equals(source.Trim(), compare.Trim(), StringComparison.InvariantCultureIgnoreCase);
    }

    public static bool MatchesRegex(this string inputString, string matchPattern)
    {
      return Regex.IsMatch(inputString, matchPattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
    }

    /// <summary>
    /// Strips the last specified chars from a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <param name="removeFromEnd">The remove from end.</param>
    /// <returns></returns>
    public static string Chop(this string sourceString, int removeFromEnd)
    {
      string result = sourceString;

      if ((removeFromEnd > 0) && (sourceString.Length > removeFromEnd - 1))
        result = result.Remove(sourceString.Length - removeFromEnd, removeFromEnd);

      return result;
    }

    /// <summary>
    /// Strips the last specified chars from a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <param name="backDownTo">The back down to.</param>
    /// <returns></returns>
    public static string Chop(this string sourceString, string backDownTo)
    {
      int removeDownTo = sourceString.LastIndexOf(backDownTo);
      int removeFromEnd = 0;

      if (removeDownTo > 0) removeFromEnd = sourceString.Length - removeDownTo;

      string result = sourceString;

      if (sourceString.Length > removeFromEnd - 1)
        result = result.Remove(removeDownTo, removeFromEnd);

      return result;
    }

    /// <summary>
    /// Plurals to singular.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string PluralToSingular(this string sourceString)
    {
      return sourceString.MakeSingular();
    }

    /// <summary>
    /// Singulars to plural.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string SingularToPlural(this string sourceString)
    {
      return sourceString.MakePlural();
    }

    /// <summary>
    /// Make plural when count is not one
    /// </summary>
    /// <param name="number">The number of things</param>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string Pluralize(this int number, string sourceString)
    {
      string ret = null;

      if (number == 1) ret = number.Concatinate(" ", sourceString.MakeSingular());
      else ret = number.Concatinate(" ", sourceString.MakePlural());

      return ret;
    }

    #region String.Concat Methods
    #region Objects
    public static string Concatinate(this string value, object arg0)
    {
      return string.Concat(value, arg0);
    }

    public static string Concatinate(this string value, object arg0, object arg1)
    {
      return string.Concat(value, arg0, arg1);
    }

    public static string Concatinate(this string value, object arg0, object arg1, object arg2)
    {
      return string.Concat(value, arg0, arg1, arg2);
    }
    #endregion

    #region Strings
    public static string Concatinate(this string value, string arg0)
    {
      return string.Concat(value, arg0);
    }

    public static string Concatinate(this string value, string arg0, string arg1)
    {
      return string.Concat(value, arg0, arg1);
    }

    public static string Concatinate(this string value, string arg0, string arg1, string arg2)
    {
      return string.Concat(value, arg0, arg1, arg2);
    }
    #endregion
    #endregion

    /// <summary>
    /// Removes the specified chars from the beginning of a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <param name="removeFromBeginning">The remove from beginning.</param>
    /// <returns></returns>
    public static string Clip(this string sourceString, int removeFromBeginning)
    {
      string result = sourceString;

      if (sourceString.Length > removeFromBeginning)
        result = result.Remove(0, removeFromBeginning);

      return result;
    }

    /// <summary>
    /// Removes chars from the beginning of a string, up to the specified string
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <param name="removeUpTo">The remove up to.</param>
    /// <returns></returns>
    public static string Clip(this string sourceString, string removeUpTo)
    {
      int removeFromBeginning = sourceString.IndexOf(removeUpTo);
      string result = sourceString;

      if (sourceString.Length > removeFromBeginning && removeFromBeginning > 0)
        result = result.Remove(0, removeFromBeginning);

      return result;
    }

    /// <summary>
    /// Strips the last char from a a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string Chop(this string sourceString)
    {
      return Chop(sourceString, 1);
    }

    /// <summary>
    /// Strips the last char from a a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string Clip(this string sourceString)
    {
      return Clip(sourceString, 1);
    }

    /// <summary>
    /// Fasts the replace.
    /// </summary>
    /// <param name="original">The original.</param>
    /// <param name="pattern">The pattern.</param>
    /// <param name="replacement">The replacement.</param>
    /// <returns></returns>
    public static string FastReplace(this string original, string pattern, string replacement)
    {
      return FastReplace(original, pattern, replacement, StringComparison.InvariantCultureIgnoreCase);
    }

    /// <summary>
    /// Fasts the replace.
    /// </summary>
    /// <param name="original">The original.</param>
    /// <param name="pattern">The pattern.</param>
    /// <param name="replacement">The replacement.</param>
    /// <param name="comparisonType">Type of the comparison.</param>
    /// <returns></returns>
    public static string FastReplace(this string original, string pattern, string replacement, StringComparison comparisonType)
    {
      if (original == null)
        return null;

      if (pattern.IsNullOrEmpty())
        return original;

      int lenPattern = pattern.Length;
      int idxPattern = -1;
      int idxLast = 0;

      var result = new StringBuilder();

      while (true)
      {
        idxPattern = original.IndexOf(pattern, idxPattern + 1, comparisonType);

        if (idxPattern < 0)
        {
          result.Append(original, idxLast, original.Length - idxLast);
          break;
        }

        result.Append(original, idxLast, idxPattern - idxLast);
        result.Append(replacement);

        idxLast = idxPattern + lenPattern;
      }

      return result.ToString();
    }

    /// <summary>
    /// Returns text that is located between the startText and endText tags.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <param name="startText">The text from which to start the crop</param>
    /// <param name="endText">The endpoint of the crop</param>
    /// <returns></returns>
    public static string Crop(this string sourceString, string startText, string endText)
    {
      int startIndex = sourceString.IndexOf(startText, StringComparison.CurrentCultureIgnoreCase);

      if (startIndex == -1) return String.Empty;

      startIndex += startText.Length;

      int endIndex = sourceString.IndexOf(endText, startIndex, StringComparison.CurrentCultureIgnoreCase);

      if (endIndex == -1) return String.Empty;

      return sourceString.Substring(startIndex, endIndex - startIndex);
    }

    /// <summary>
    /// Removes excess white space in a string.
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string Squeeze(this string sourceString)
    {
      char[] delim = { ' ' };
      string[] lines = sourceString.Split(delim, StringSplitOptions.RemoveEmptyEntries);
      var sb = new StringBuilder();

      foreach (string s in lines)
      {
        if (s.Trim().HasValue()) sb.Append(s + " ");
      }

      return Chop(sb.ToString()).Trim();
    }

    /// <summary>
    /// Removes all non-alpha numeric characters in a string
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string ToAlphaNumericOnly(this string sourceString)
    {
      return sourceString.ReplaceRegex(RegexPattern.NON_WORD, "");
    }

    /// <summary>
    /// Creates a string array based on the words in a sentence
    /// </summary>
    /// <param name="sourceString">The source string.</param>
    /// <returns></returns>
    public static string[] ToWords(this string sourceString)
    {
      return sourceString.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
    }

    /// <summary>
    /// Strips all HTML tags from a string
    /// </summary>
    /// <param name="htmlString">The HTML string.</param>
    /// <returns></returns>
    public static string StripHTML(this string htmlString)
    {
      return StripHTML(htmlString, String.Empty);
    }

    /// <summary>
    /// Strips all HTML tags from a string and replaces the tags with the specified replacement
    /// </summary>
    /// <param name="htmlString">The HTML string.</param>
    /// <param name="htmlPlaceHolder">The HTML place holder.</param>
    /// <returns></returns>
    public static string StripHTML(this string htmlString, string htmlPlaceHolder)
    {
      string sOut = htmlString.ReplaceRegex(RegexPattern.TAG, htmlPlaceHolder);

      sOut = sOut.Replace("&nbsp;", String.Empty);
      sOut = sOut.Replace("&amp;", "&");
      sOut = sOut.Replace("&gt;", ">");
      sOut = sOut.Replace("&lt;", "<");

      return sOut;
    }

    /// <summary>
    /// Replaces text in the string using a regex. Simplification of Regex.Replace
    /// </summary>
    /// <param name="input"></param>
    /// <param name="regex"></param>
    /// <param name="replacement"></param>
    /// <returns></returns>
    public static string ReplaceRegex(this string input, string regex, string replacement)
    {
      return Regex.Replace(input, regex, replacement);
    }

    /// <summary>
    /// Replaces text in the string using a regex. Simplification of Regex.Replace with options.
    /// </summary>
    /// <param name="input"></param>
    /// <param name="regex"></param>
    /// <param name="replacement"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static string ReplaceRegex(this string input, string regex, string replacement, RegexOptions options)
    {
      return Regex.Replace(input, regex, replacement, options);
    }

    public static List<string> FindMatches(this string source, string find)
    {
      var reg = new Regex(find, RegexOptions.IgnoreCase);

      var result = new List<string>();

      foreach (Match m in reg.Matches(source)) result.Add(m.Value);

      return result;
    }

    /// <summary>
    /// Converts a generic List collection to a single comma-delimitted string.
    /// </summary>
    /// <param name="list">The list.</param>
    /// <returns></returns>
    public static string ToDelimitedList(this IEnumerable<string> list)
    {
      return ToDelimitedList(list, ",");
    }
    #endregion
  }
}
